﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using WpfWordsLearner.Model;
using System.Collections.ObjectModel;

namespace WpfWordsLearner
{
	public class WordsManager
	{
		private static readonly WordsManager m_manager;
		private XDocument m_doc = null;
		private static string m_pathToDoc;

		internal List<WordsModel> WordsToSave { get; set; }

		static WordsManager()
		{
			m_manager = new WordsManager();
		}

		private WordsManager()
		{
		}

		public static WordsManager Instance
		{
			get
			{
				return m_manager;
			}
		}

		public string PathToDictionary
		{
			get { return m_pathToDoc; }
		}

		public bool AddWord(string english, string translate)
		{
			if (string.IsNullOrEmpty(english) || string.IsNullOrEmpty(translate))
				return false;
			try
			{
				m_doc.Root.Add(new XElement("Word",
										new XElement("English", EncryptionManager.Encrypt(english)),
										new XElement("Translate", EncryptionManager.Encrypt(translate)),
										new XElement("Complicated", "False")));
				this.SaveAndReload();
				return true;
			}
			catch { }
			return false;
		}

		public bool AddWordRange(List<Model.WordsModel> words)
		{
			if (words == null || words.Count == 0)
				return false;
			try
			{
				foreach (Model.WordsModel word in words)
				{
					m_doc.Root.Add(new XElement("Word",
											new XElement("English", EncryptionManager.Encrypt(word.EnglishWord)),
											new XElement("Translate", EncryptionManager.Encrypt(word.Translate)),
											new XElement("Complicated", word.IsComplicated)
											));
				}
				this.SaveAndReload();
				return true;
			}
			catch { }
			return false;
		}

		public string GetTranslateWord(string english)
		{
			try
			{
				string data = (from item in m_doc.Root.Elements("Word")
							   where item.Element("English").Value.Contains(english)
							   select item.Element("Translate").Value).FirstOrDefault();
				return string.IsNullOrEmpty(data) ? string.Empty : EncryptionManager.Decrypt(data);
			}
			catch { }
			return string.Empty;
		}

		public string GetEnglishWord(string translate)
		{
			try
			{
				string data = (from item in m_doc.Root.Elements("Word")
							   where item.Element("Translate").Value.Contains(translate)
							   select item.Element("English").Value).FirstOrDefault();
				return string.IsNullOrEmpty(data) ? string.Empty : EncryptionManager.Decrypt(data);
			}
			catch { }
			return string.Empty;
		}

		public ObservableCollection<WordsModel> GetWordsList()
		{
			try
			{
				ObservableCollection<WordsModel> words = new ObservableCollection<WordsModel>();

				foreach (XElement element in m_doc.Root.Elements("Word"))
				{
					WordsModel model = new WordsModel();
					model.EnglishWord = EncryptionManager.Decrypt(element.Element("English").Value);
					model.Translate = EncryptionManager.Decrypt(element.Element("Translate").Value);
					model.IsComplicated = string.Compare("true", element.Element("Complicated").Value, true) == 0;
					words.Add(model);
				}

				//IEnumerable<WordsModel> list = (from item in m_doc.Root.Elements("Word")
				//                                select new WordsModel()
				//                                {
				//                                    EnglishWord = EncryptionManager.Decrypt(item.Element("English").Value),
				//                                    Translate = EncryptionManager.Decrypt(item.Element("Translate").Value),
				//                                    IsComplicated = string.Compare("true", item.Element("Complicated").Value, true) == 0
				//                                });
				////DAO.WordsDao dao = new DAO.WordsDao(App.pathToDB);
				//foreach (WordsModel model in list)
				//{

				//    //if (!dao.ContainsWord(model.EnglishWord))
				//    //    dao.CreateWord(new DAO.Words() { Word = model.EnglishWord, Translation = model.Translate, Complicated = model.IsComplicated ? 1 : 0 });
				//    words.Add(model);
				//}
				return words;
			}
			catch { }
			return null;
		}

		public List<WordsModel> GetWordsListFromFile(string file)
		{
			if (string.IsNullOrEmpty(file) || !System.IO.File.Exists(file))
				return null;
			try
			{
				List<WordsModel> words = new List<WordsModel>();

				using (System.IO.StreamReader sr = new System.IO.StreamReader(file))
				{
					string readLine = string.Empty;
					while ((readLine = sr.ReadLine()) != null)
					{
						if (string.IsNullOrEmpty(readLine) || string.IsNullOrWhiteSpace(readLine) || !readLine.Contains("-"))
							continue;

						int indexOF = readLine.IndexOf('-');
						if (indexOF == -1)
							continue;

						var word = new WordsModel();
						word.EnglishWord = readLine.Substring(0, indexOF).Trim();
						word.Translate = readLine.Substring(indexOF + 1, (readLine.Length - indexOF) - 1).Trim();
						word.IsComplicated = false;

						words.Add(word);
					}
					return words;
				}
			}
			catch (Exception ex) { App.Log.Error("GetWordsListFromFile", ex); }
			return null;
		}

		public static List<WordsModel> GetWordsList(string pathToDoc)
		{
			if (string.IsNullOrEmpty(pathToDoc))
				return null;
			try
			{
				List<WordsModel> words = null;

				words = (from item in XDocument.Load(pathToDoc).Root.Elements("Word")
						 select new WordsModel()
						 {
							 EnglishWord = EncryptionManager.Decrypt(item.Element("English").Value),
							 Translate = EncryptionManager.Decrypt(item.Element("Translate").Value),
							 IsComplicated = string.Compare("true", item.Element("Complicated").Value, true) == 0
						 }).ToList();
				return words;
			}
			catch { }
			return null;
		}

		public int WorsdCount
		{
			get
			{
				if (m_doc == null)
					throw new InvalidOperationException("m_doc is NULL!");
				return m_doc.Root.Elements("Word").Count();
			}
		}

		private void SaveAndReload()
		{
			if (!string.IsNullOrEmpty(m_pathToDoc))
			{
				m_doc.Save(m_pathToDoc);
				m_doc = XDocument.Load(m_pathToDoc);
			}
		}

		public void FinalSave()
		{
			if (!string.IsNullOrEmpty(m_pathToDoc) && WordsToSave != null)
			{
				m_doc.Root.RemoveAll();
				foreach (WordsModel word in this.WordsToSave)
				{
					m_doc.Root.Add(new XElement("Word",
											new XElement("English", EncryptionManager.Encrypt(word.EnglishWord)),
											new XElement("Translate", EncryptionManager.Encrypt(word.Translate)),
											new XElement("Complicated", word.IsComplicated)
											));
				}
				m_doc.Save(m_pathToDoc);
			}
		}

		public void SaveAsEncrypted(List<WordsModel> list, string fname)
		{
			if (list == null || list.Count == 0 || string.IsNullOrEmpty(fname))
				return;

			if (System.IO.File.Exists(fname))
				System.IO.File.Delete(fname);

			XDocument doc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"), new XElement("Words"));

			foreach (WordsModel word in list)
			{
				doc.Root.Add(new XElement("Word",
										new XElement("English", EncryptionManager.Encrypt(word.EnglishWord)),
										new XElement("Translate", EncryptionManager.Encrypt(word.Translate)),
										new XElement("Complicated", word.IsComplicated.ToString())));
			}

			doc.Save(fname);
			doc = null;
		}

		public void SetPreference(string xmlDoc)
		{
			if (string.IsNullOrEmpty(xmlDoc) || !System.IO.File.Exists(xmlDoc))
				throw new ArgumentNullException("xmlDoc");
			m_doc = null;
			m_doc = XDocument.Load(xmlDoc);
			m_pathToDoc = xmlDoc;
		}

		public static List<Model.TestWordModel> GenerateTestWords(List<int> indxs)
		{
			XDocument xmlDoc = XDocument.Load(m_pathToDoc);

			List<Model.TestWordModel> resultSet = new List<TestWordModel>();
			foreach (int ind in indxs)
			{
				XElement node = xmlDoc.Root.Elements("Word").ElementAt(ind);
				Model.TestWordModel model = new TestWordModel();
				model.Word = new WordsModel();
				model.Word.EnglishWord = EncryptionManager.Decrypt(node.Element("English").Value);
				model.Word.Translate = EncryptionManager.Decrypt(node.Element("Translate").Value);
				model.Word.IsComplicated = string.Compare(node.Element("Complicated").Value, "true", true) == 0;
				resultSet.Add(model);
			}
			xmlDoc = null;
			return resultSet;
		}

		public static List<Model.LearnItem> GenerateWordsToLearn(List<int> indxs, bool onlyComplicated = false, int wordCount = 0)
		{
			List<Model.LearnItem> resultSet = new List<LearnItem>();

			if (!onlyComplicated)
			{
				System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
				xmlDoc.Load(m_pathToDoc);
				foreach (int ind in indxs)
				{
					System.Xml.XmlNode node = xmlDoc.DocumentElement.ChildNodes[ind];
					Model.LearnItem model = new LearnItem();
					model.Word = EncryptionManager.Decrypt(node.FirstChild.InnerText);
					model.Translate = EncryptionManager.Decrypt(node.ChildNodes[1].InnerText);
					model.IsComplicated = string.Compare(node.ChildNodes[2].InnerText, "true", true) == 0;

					resultSet.Add(model);
				}
				xmlDoc = null;
			}
			else
			{
				XDocument doc = XDocument.Load(m_pathToDoc);
				IEnumerable<XElement> elements = doc.Root.Elements("Word").Where(x => string.Compare(x.Element("Complicated").Value, "true") == 0);
				int countOfComplicated = elements.Count();
				if (countOfComplicated < wordCount)
					return null;

				List<int> wordsIndexes = new List<int>();
				Random r = new Random();
				for (int i = 0; i < wordCount; i++)
				{
					int wordIndx = r.Next(0, countOfComplicated);
					while (wordsIndexes.Contains(wordIndx))
					{
						wordIndx = r.Next(0, countOfComplicated);
					}
					wordsIndexes.Add(wordIndx);
				}

				foreach (int indx in wordsIndexes)
				{
					XElement node = elements.ElementAt(indx);
					if (node == null) continue;

					Model.LearnItem model = new LearnItem();
					model.Word = EncryptionManager.Decrypt(node.Element("English").Value);
					model.Translate = EncryptionManager.Decrypt(node.Element("Translate").Value);
					model.IsComplicated = string.Compare(node.Element("Complicated").Value, "true", true) == 0;

					resultSet.Add(model);

					if (resultSet.Count == wordCount)
						break;
				}

				doc = null;
			}

			return resultSet;
		}

		public void SaveChanges()
		{
			SaveAndReload();
		}

		public void Import(List<Model.WordsModel> words)
		{
			if (words == null || words.Count == 0)
				return;

			bool isExceptional = false;

			try
			{
				System.IO.File.Copy(m_pathToDoc, System.IO.Path.ChangeExtension(m_pathToDoc, "backup"), true);
				System.IO.File.Delete(m_pathToDoc);

				XDocument doc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"), new XElement("Words"));

				foreach (Model.WordsModel word in words)
				{
					doc.Root.Add(new XElement("Word",
											new XElement("English", EncryptionManager.Encrypt(word.EnglishWord)),
											new XElement("Translate", EncryptionManager.Encrypt(word.Translate)),
											new XElement("Complicated", word.IsComplicated.ToString())));
				}

				doc.Save(m_pathToDoc);
				m_doc = null;
				m_doc = doc;
				isExceptional = false;
			}
			catch (System.IO.IOException ex) { App.Log.Error("System.IO.IOException", ex); isExceptional = true; }
			catch (System.Exception ex) { App.Log.Error("System.Exception", ex); isExceptional = true; }
			finally
			{
				if (isExceptional)
					System.IO.File.Copy(System.IO.Path.ChangeExtension(m_pathToDoc, "backup"), m_pathToDoc, true);

				GC.Collect(0);
			}
		}
	}
}
